Sample Groovy

Please note that the groovy below is for example use, it will need to be adjusted for target requirements and also to cover failure scenarios

            import java.util.Map;

            import com.Navis.apex.business.model.GroovyInjectionBase;

            import com.Navis.framework.persistence.HibernateApi;

            import com.Navis.framework.portal.FieldChanges;

            import com.Navis.framework.util.BizViolation;

            import com.Navis.inventory.InventoryBizMetafield;

            import com.Navis.inventory.InventoryField;

            import com.Navis.inventory.business.units.EqBaseOrderItem;

            import com.Navis.inventory.business.units.Unit;

            import com.Navis.inventory.web.InventoryMobileUtil;

            import com.Navis.orders.business.eqorders.EquipmentOrderItem;

            /**

            * This is an example Groovy class and is not readily production deployable. The idea is to provide a simple template that can be

            * extended by the deployers.

            * As the program below shows

            * (1) Same (this) groovy class is called by each of the RDT programs (except Gate for which this plugin is not supported).

            * (2) Each call comes with a sete of parameters including the Application Names (Rail, Yard, Hatch, Rail Inv, Reefer etc)

            * (3) While most of the programs get an entity as part of the arguments, but entity can be null and should be handled as such.

            *   Specially in case of rail inventory

            *   there can be situations where entity is not created (or retrieved from the database) because no action is needed on a

            *   particular slot or because user entered possibly

            *   a wrong container number which would be caught later by the prebuilt product validations.

            * This class is called when RDT's submit (commit) changes. The usecase is specfically applicable during inspection data

            * posting that may need to go through additional

            * validations that are not part of the product (out of the box)

            */

            public class RdtCustomGroovyImpl extends GroovyInjectionBase {

              final String CLASS_NAME = "RdtCustomGroovyImpl: ";

              public void log(String inMsg){

                super.log(CLASS_NAME+ inMsg );

              }

              public void validateChanges(Map args) throws BizViolation {

                if (ignoreAllOverrideableErrors()) {

                  log("skipping groovy validations as override flag is set");

                  return;

                } else {

                  if (args==null){

                    log("skipping groovy validations as required parameters are missing");

                    return;

                  }

                  String appName = (String) args.get(InventoryBizMetafield.RDT_APPLICATION_NAME);

                  if (appName==null){

                    log("skipping groovy validations as app is not set");

                    return;

                  }

                  Object entity = args.get(InventoryBizMetafield.RDT_ENTITY);

                  FieldChanges fcs = (FieldChanges) args.get(InventoryBizMetafield.RDT_FORM_CHANGES);

                  log(appName);

                  Unit unit;

                  // Hatch Clerk Program

                  if (appName.equals(InventoryMobileUtil.HATCH_CLERK_PROGRAM_NAME)) {

                    doExtraValidations(entity, fcs, appName);

                  // Rail or Yard Inspection Program get the same app name and we distinguish based on

                  // the container location.

                  } else if (appName.equals(InventoryMobileUtil.RAIL_OR_YARD_INSPECTION_PROGRAM_NAME)) {

                    unit = (Unit) entity;

                    if (unit.isUnitInYard()) {

                      log("Yard Inspection Program");

                    } else {

                      log(" Rail Inspection Program");

                    }

                    doExtraValidations(entity, fcs, appName);

                  // Rail Inventory program iterates over the rail car slots and makes

                  // a decision about load, position correction, removal, bump, swap etc.

                  // The example code below shows what is avalable in the parameters.

                  } else if (appName.equals(InventoryMobileUtil.RAIL_INVENTORY_PROGRAM_NAME)) {

                    unit = (Unit) entity;

                    String newPosName = (String) fcs.getFieldChange(InventoryField.POS_NAME).getNewValue();

                    String newUnitId = (String) fcs.getFieldChange(InventoryField.UNIT_ID).getNewValue();

                    String oldUnitId = (String) fcs.getFieldChange(InventoryField.UNIT_ID).getPriorValue();

                    log("=== Rail Inventory Program: New Unit Id: " + newUnitId);

                    log("=== Rail Inventory Program: New Position " + newPosName);

                    if (newUnitId == null || newUnitId.trim().length() == 0) {

                      log("Non-LOAD operation, returning without validations for: " + newPosName);

                      return;

                    }

                    if (newUnitId.equals(oldUnitId)) {

                      log("No change in the position, allowing the operation: " + newPosName);

                      return;

                    }

                    if (unit != null) {

                      log("TODO, Add additional Antwerp validation on unit " + newUnitId);

                      return;

                    } else {

                      log("TODO, N4 Could not locate a Unit..Returning " + newUnitId);

                      return;

                    }

                  } else if (appName.equals(InventoryMobileUtil.REEFER_MONITORING_PROGRAM_NAME)) {

                    doExtraValidations(entity, fcs, appName);

                  } else {

                    log("Uknown program type, skipping custom validations");

                  }

                }

              }

              /**

              * Internal Helper Method

              * @param inEntity entity being updated or loaded. Can be NULL

              * @param inFcs field changes

              * @param inAppName Hand-held application ID.

              */

              void doExtraValidations(Object inEntity, com.Navis.framework.portal.FieldChanges inFcs, String inAppName) {

                Unit unit = (Unit) inEntity;

                // by reading from the unit we get the new values applied in memory (not persisted yet)

                Object seal = unit.getFieldValue(InventoryField.UNIT_SEAL_NBR1);

                log("========== Unit Seal1:" + seal);

                Boolean pm = (Boolean) unit.getFieldValue(InventoryField.UNIT_ARE_PLACARDS_MISMATCHED);

                log(" ========== Unit Placard Mismatch?:" + pm);

                if (pm != null && pm.booleanValue()) {

                  registerOverridableError("Required placards don't match with observed placards");

                }

                Object oogBack = unit.getFieldValue(InventoryField.UNIT_OOG_BACK_CM);

                log("=========== Unit OOG Back:" + oogBack);

                String eqType = "";

                if (unit.getUnitPrimaryUe().getUeEquipment().getEqEquipType() != null) {

                  eqType = (String) unit.getUnitPrimaryUe().getUeEquipment().getEqEquipType().getEqtypId();

                  log("======== Unit ISO Code Being Updated: " + eqType);

                }

                // booking values

                if (unit.getUnitPrimaryUe().getUeDepartureOrderItem() != null) {

                  EqBaseOrderItem bIso = unit.getUnitPrimaryUe().getUeDepartureOrderItem();

                  EquipmentOrderItem eqoi = (EquipmentOrderItem) HibernateApi.getInstance().downcast(bIso, EquipmentOrderItem.class);

                  log("======== Booking Reference : " + eqoi);

                  if (eqoi != null) {

                    String bIsoStr = eqoi.getEqoiSampleEquipType().getEqtypId();

                    log("Booking ISO Code: " + bIsoStr);

                    if (!bIsoStr.equals(eqType)) {

                      registerOverridableError("booking ISO Code: " + bIsoStr + " doesn't seem to match with unit value:" + eqType);

                    }

                  }

                }

              }

            }

Example showing how to extract POW, CHE related fields from the parameters supplied to the Groovy Script:

import com.Navis.apex.business.model.GroovyInjectionBase

import com.Navis.framework.util.BizViolation

import com.Navis.inventory.InventoryBizMetafield

import com.Navis.inventory.business.atoms.RailInspectionOperationsEnum

import com.Navis.inventory.web.InventoryMobileUtil

import com.Navis.rail.variform.RailMobileVariforms

import com.Navis.rail.web.RailMobileGuiMetafield

class RdtCustomGroovyImpl extends GroovyInjectionBase {

// Main function called to perform the validations

void validateChanges(Map args) throws BizViolation {

  this.log("validateChanges ignoreAllOverrideableErrors=${this.ignoreAllOverrideableErrors()}")

  if (args == null) {

   this.log('args cannot be null')

   return

  }

  // We first need to find out what RDT program is calling us

  def programName = args.get(InventoryBizMetafield.RDT_APPLICATION_NAME)

  this.log("programName=$programName")

  if (programName == null) {

   this.log('exiting due to null programName')

   return

  }

  // we retrieve the field changes. This is critical for determining what was modified by the RD user

  def fieldChanges = args.get(InventoryBizMetafield.RDT_FORM_CHANGES)

  //this.printFieldChanges(fieldChanges)

  // see if variform data is present. Currently we populate that only for the Discharge operation.

  def mobileFormData = fieldChanges.getFieldChange(InventoryBizMetafield.MOBILE_FORM_DATA)

  this.log("Mobile form data=$mobileFormData")

  if (mobileFormData == null) {

   this.log('No Form data. Exiting')

   return

  }

  if (InventoryMobileUtil.RAIL_OR_YARD_INSPECTION_PROGRAM_NAME.equals(programName)) {

   def operation = fieldChanges.getFieldChange(InventoryBizMetafield.MOBILE_OPERATION_NAME).getNewValue()

   this.log("Rail Operation =" + operation)

   if (RailInspectionOperationsEnum.DISCHARGE.equals(operation)) {

    //get Pow

    def powFcs = mobileFormData.getNewValue().get(RailMobileVariforms.MRI017)

    if (powFcs != null) {

     def pow = powFcs.getFieldChange(RailMobileGuiMetafield.MR_CRANE_I_D).getNewValue()

     this.log("Rail POW (Discharge) = $pow")

    }

   } else if (RailInspectionOperationsEnum.LOAD.equals(operation)) {

    //get Pow

    def powFcs = mobileFormData.getNewValue().get(RailMobileVariforms.MRI017)

    if (powFcs != null) {

     def pow = powFcs.getFieldChange(RailMobileGuiMetafield.MR_CRANE_I_D).getNewValue()

     this.log("Rail POW (Load) = $pow")

    }

   }

  }

}

}

Example showing how to stop dispatches to an inactive truck using N4 Mobile:

import com.Navis.apex.business.model.GroovyInjectionBase;

import com.Navis.argo.business.atoms.CheStatusEnum;

import com.Navis.argo.business.xps.model.Che;

import com.Navis.framework.portal.FieldChanges;

import com.Navis.framework.util.BizViolation;

import com.Navis.inventory.InventoryBizMetafield;

import com.Navis.inventory.business.api.InventoryCargoUtils;

import com.Navis.inventory.web.InventoryMobileUtil;

import com.Navis.vessel.business.operation.Vessel;

import com.Navis.vessel.variform.VesselMobileVariforms;

import com.Navis.vessel.web.VesselMobileGuiMetafield;

class RdtCustomGroovyImpl extends GroovyInjectionBase {

  void validateChanges(Map args) throws BizViolation {

    if (args == null) {

      this.log("args cannot be null");

      return;

    }

    Object programName = args.get(InventoryBizMetafield.RDT_APPLICATION_NAME);

    if (programName == null) {

      this.log("exiting due to null programName");

      return;

    }

    FieldChanges fieldChanges = (FieldChanges) args.get(InventoryBizMetafield.RDT_FORM_CHANGES);

    def mobileFormData = fieldChanges.getFieldChange(InventoryBizMetafield.MOBILE_FORM_DATA);

    if (mobileFormData == null) {

      this.log('No Form data. Exiting');

      return;

    }

    def dischStatus = mobileFormData.getNewValue().get(VesselMobileVariforms.MNHCDschStatus);

    String cheName = null;

    if (dischStatus != null) {

      cheName = dischStatus.getFieldChange(VesselMobileGuiMetafield.MNHC_LANE).getNewValue();

    }

    if (cheName != null) {

      Che crane = InventoryCargoUtils.resolveCheByShortName(cheName);

      if (crane != null && crane.getCheStatusEnum() != CheStatusEnum.WORKING) {

        registerError("Truck is inactive state, cannot discharge");

      }

    }

  }

}

Example showing how to stop dispatches to an inactive truck during discharge, load, or shift operation using the Manual Ops form in Crane Team UI (CTUI):

import com.Navis.apex.business.model.GroovyInjectionBase

import com.Navis.crane.presentation.view.manops.ManualOpsGroupFetch

import com.Navis.crane.variform.CraneVariforms

import com.Navis.framework.persistence.HibernateApi

import com.Navis.framework.portal.FieldChange

import com.Navis.framework.portal.FieldChanges

import com.Navis.framework.util.BizViolation

import com.Navis.inventory.InventoryBizMetafield

import com.Navis.inventory.InventoryField

import com.Navis.inventory.business.units.EqBaseOrderItem

import com.Navis.inventory.business.units.Unit

import com.Navis.inventory.portal.ApiConsts

import com.Navis.inventory.web.InventoryMobileUtil

import com.Navis.orders.business.eqorders.EquipmentOrderItem

public class RdtCustomGroovyImpl extends GroovyInjectionBase {

  final String CLASS_NAME = "RdtCustomGroovyImpl: ";

  public void log(String inMsg) {

    super.log(CLASS_NAME + inMsg);

  }

  public void validateChanges(Map args) throws BizViolation {

    if (ignoreAllOverrideableErrors()) {

      log("skipping groovy validations as override flag is set");

      return;

    } else {

      if (args == null) {

        log("skipping groovy validations as required parameters are missing");

        return;

      }

      String appName = (String) args.get(InventoryBizMetafield.RDT_APPLICATION_NAME);

      if (appName == null) {

        log("skipping groovy validations as app is not set");

        return;

      }

      Object entity = args.get(InventoryBizMetafield.RDT_ENTITY);

      FieldChanges fcs = (FieldChanges) args.get(InventoryBizMetafield.RDT_FORM_CHANGES);

      log(appName);

      Unit unit;

      // Hatch Clerk Program

      if (appName.equals(InventoryMobileUtil.HATCH_CLERK_PROGRAM_NAME)) {

        FieldChange operNameFC = (FieldChange)fcs.getFieldChange(InventoryBizMetafield.MOBILE_OPERATION_NAME);

        String opName = (String)operNameFC.getNewValue();

        //Check if Groovy is called from CTUI or N4 Mobile

        if (isCraneTeamUIHatchClerk(opName)){ //for CTUI

          doCraneTeamValidations(entity, fcs, appName,opName );

        }else { // for N4Mobile

         

        }

        // Rail or Yard Inspection Program get the same app name and we distinguish based on

        // the container location.

     

      } else {

        // this is for unit test purpose

        log("Uknown program type, skipping custom validations");

        println("Uknown program type, skipping custom validations");

        registerOverridableError("booking values don't seem to match with unit values");

      }

    }

  }

  /**

   * Internal Helper Method

   *

   * @param inOpName Operation name

   * @return boolean return whether the groovy called from CTUI or N4 Mobile

   */

  private boolean isCraneTeamUIHatchClerk(String inOpName){

    boolean isCraneTeamUI = false;

    if ((CraneVariforms.FORM_MANUAL_OPS_DISCHARGE.equals(inOpName))

        || (CraneVariforms.FORM_MANUAL_OPS_LOAD.equals(inOpName))

        || (CraneVariforms.FORM_MANUAL_OPS_SHIFT.equals(inOpName))){

      isCraneTeamUI = true;

    }

    return isCraneTeamUI;

  }

  /**

   * Internal Helper Method

   *

   * @param inEntity entity being updated or loaded. Can be NULL

   * @param inFcs   field changes

   * @param inAppName Hand-held application ID.

   */

  private void doCraneTeamValidations(Object inEntity, com.Navis.framework.portal.FieldChanges inFcs, String inAppName, String inOpName ){

    FieldChange manualOpsGroupFetchFC = (FieldChange) inFcs.getFieldChange(InventoryBizMetafield.MOBILE_FORM_DATA);

    Map<String, ManualOpsGroupFetch> manualOpsGroupFetchMap = (Map<String, ManualOpsGroupFetch>) manualOpsGroupFetchFC.getNewValue();

    ManualOpsGroupFetch manualOpsGroupFetch = manualOpsGroupFetchMap.get(ApiConsts.MANUAL_OPS_GROUP_FETCH);

    String mode = manualOpsGroupFetch.getLiftMode().getOpMode().getKey();

    log("CTUI mode " + mode);

    if ((CraneVariforms.FORM_MANUAL_OPS_DISCHARGE.equals(inOpName))) {

      log("Groovy called from Discharge Manual Operation screen in CTUI ");

    }else if ((CraneVariforms.FORM_MANUAL_OPS_LOAD.equals(inOpName))){

      log("Groovy called from Load Manual Operation screen in CTUI ");

    }else if ((CraneVariforms.FORM_MANUAL_OPS_SHIFT.equals(inOpName))){

      log("Groovy called from Shift Manual Operation screen in CTUI ");

    }

  }

}

Please note the following aspects:

The groovy class is extending the GroovyInjectionBase to inherit the prebuilt utility methods to log and to register overridable errors.